const std = @import("std");
const os = @import("../os.zig");
const log = std.log.scoped(.rtc);
const driver = @import("../driver.zig");
const epoch = std.time.epoch;

comptime {
    driver.register(&init, "google,goldfish-rtc");
}

var rtc: os.lib.Mmio(goldfish, false) = .empty;

fn init(arg: ?*const anyopaque) void {
    const node: *const os.fdt.Node = @ptrCast(@alignCast(arg orelse return));
    const base = os.fdt.base(node);
    rtc = .init(base);
    log.debug("UTC {f}", .{date()});
}

// https://github.com/torvalds/linux/blob/master/drivers/rtc/rtc-goldfish.c
const goldfish = enum(usize) {
    pub const Int = u32;

    /// get low bits of current time and update time_high
    time_low = 0x00,
    /// get high bits of time at last TIMER_TIME_LOW read
    time_high = 0x04,
};

/// Get a calendar timestamp, in ns, relative to UTC 1970-01-01.
pub fn nanoTimestamp() i128 {
    const low: u64 = rtc.read(.time_low);
    const high: u64 = rtc.read(.time_high);
    return (high << 32) | low;
}

/// Get a calendar timestamp, in us, relative to UTC 1970-01-01.
pub fn microTimestamp() i64 {
    return @intCast(@divFloor(nanoTimestamp(), std.time.ns_per_us));
}

/// Get a calendar timestamp, in ms, relative to UTC 1970-01-01.
pub fn milliTimestamp() i64 {
    return @divFloor(microTimestamp(), std.time.us_per_ms);
}

/// Get a calendar timestamp, in seconds, relative to UTC 1970-01-01.
pub fn timestamp() i64 {
    return @divFloor(milliTimestamp(), std.time.ms_per_s);
}

pub const Time = struct {
    Y: epoch.Year,
    M: epoch.Month,
    D: u5, // days into the month (1 to 31)
    h: u6, // hours into the day (0 to 23)
    m: u6, // minutes into the hour (0 to 59)
    s: u6, // seconds into the minute (0 to 59)

    const Writer = std.Io.Writer;
    pub fn format(self: Time, w: *Writer) Writer.Error!void {
        try w.print(
            "{}/{:0>2}/{:0>2} {:0>2}:{:0>2}:{:0>2}",
            .{ self.Y, @intFromEnum(self.M), self.D, self.h, self.m, self.s },
        );
    }
};

pub fn date() Time {
    const epoch_seconds: epoch.EpochSeconds = .{ .secs = @intCast(timestamp()) };
    const epoch_day = epoch_seconds.getEpochDay();
    const year_day = epoch_day.calculateYearDay();
    const month_day = year_day.calculateMonthDay();

    const day_seconds = epoch_seconds.getDaySeconds();
    const hour = day_seconds.getHoursIntoDay();
    const minute = day_seconds.getMinutesIntoHour();
    const second = day_seconds.getSecondsIntoMinute();

    return .{
        .Y = year_day.year,
        .M = month_day.month,
        .D = month_day.day_index + 1,
        .h = hour,
        .m = minute,
        .s = second,
    };
}
